package site.ruyi.devclient.client.redis;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONValidator;
import com.alibaba.fastjson.TypeReference;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import redis.clients.jedis.ListPosition;
import redis.clients.jedis.Tuple;
import site.ruyi.devclient.client.redis.impl.DO.RedisDBTreeNode;
import site.ruyi.devclient.client.redis.impl.DO.RedisKeyTreeNode;
import site.ruyi.devclient.client.redis.impl.DO.RedisRootTreeNode;
import site.ruyi.devclient.core.AlertUtil;
import site.ruyi.devclient.core.FormDialog;
import site.ruyi.devclient.core.TextAreaAppender;

public class RedisClientController implements Initializable{

	/**
	 * redis key类型字符串常量
	 */
	protected static final String KEY_TYPE_STRING = "string";
	protected static final String KEY_TYPE_SET = "set";
	protected static final String KEY_TYPE_ZSET = "zset";
	protected static final String KEY_TYPE_LIST = "list";
	protected static final String KEY_TYPE_HASH = "hash";

	//TreeView树邮件菜单
	private static final MenuItem refreshItem = new MenuItem("刷新");
	private static final MenuItem disconnectItem = new MenuItem("断开连接");
	private static final MenuItem clearDBItem = new MenuItem("清理DB");
	private static final MenuItem createKeyItem = new MenuItem("创建键");
	private static final MenuItem delKeyItem = new MenuItem("删除键");
	private static final MenuItem expireItem = new MenuItem("设置有效期");
	private static final MenuItem persistItem = new MenuItem("清除有效期");
	private static final MenuItem renameItem = new MenuItem("重命名");
	private static final MenuItem setItem = new MenuItem("设置值");
	private static final MenuItem saddItem = new MenuItem("增加项");
	private static final MenuItem sremItem = new MenuItem("删除项");
	private static final MenuItem zaddItem = new MenuItem("增加项");
	private static final MenuItem zremItem = new MenuItem("删除项");
	private static final MenuItem lsetItem = new MenuItem("设置元素");
	private static final MenuItem linsertItem = new MenuItem("元素左/右插入");
	private static final MenuItem lpushItem = new MenuItem("左侧插入");
	private static final MenuItem lpopItem = new MenuItem("左侧弹出");
	private static final MenuItem rpushItem = new MenuItem("右侧插入");
	private static final MenuItem rpopItem = new MenuItem("右侧弹出");
	private static final MenuItem lremItem = new MenuItem("移除元素");
	private static final MenuItem hsetItem = new MenuItem("设置项");
	private static final MenuItem hdelItem = new MenuItem("删除项");

	/**
	 * Redis客户端名称与实例的Map集合
	 */
	private static final Map<String,IRedisClient> REDIS_CLIENT=new HashMap<String,IRedisClient>();
	static {
		//通过服务发现查找Redis客户端实例
		ServiceLoader<IRedisClient> clients=ServiceLoader.load(IRedisClient.class);
		for(IRedisClient client:clients) {
			REDIS_CLIENT.put(client.getName(), client);
		}
	}

	//应用日志对象
	private Logger appLogger;

	//命令执行线程
	private static final ExecutorService exeService=Executors.newSingleThreadExecutor();

	@FXML
	private BorderPane container;

	/**
	 * 左侧树结构
	 */
	@FXML
	private TreeView<RedisTreeNode> redisTreeView;

	/**
	 * 信息组件
	 */
	@FXML
	private TextArea infoTextArea;

	/**
	 * 命令历史组件
	 */
	@FXML
	private TextArea cmdTextArea;

	/**
	 * 命令输入框
	 */
	@FXML
	private TextField cmdTextField;

	/**
	 * 日志组件
	 */
	@FXML
	private TextArea logTextArea;

	/**
	 * redis客户端连接实例
	 */
	private IRedisClient redisClient;

	@Override
	public void initialize(URL location, ResourceBundle resources) {
		appLogger = Logger.getLogger("redis_client_app");
		appLogger.addAppender(new TextAreaAppender(logTextArea));
		appLogger.info("日志输出初始化完成！");

		Platform.runLater(()->{
			Stage stage = (Stage)container.getScene().getWindow();
			 stage.setOnCloseRequest((event)->{
				 exeService.shutdown();
				 if(redisClient!=null){
					 try {
						 redisClient.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				 }
			 });
		});
	}

	/**
	 * 选择redis客户端。
	 * 如果只有一个实例则直接使用，多个时弹出选择对话框进行选择。
	 * @return
	 */
	private IRedisClient choiceClient() {
		appLogger.info("进行客户端选择。");
		if(REDIS_CLIENT.size() == 1) {
			appLogger.info("仅有一个客户端示例，无需选择！");
			//只有一种客户端
			for(IRedisClient client:REDIS_CLIENT.values()) {
				return client;
			}
		}

		appLogger.info("启动客户端选择对话框。");
		//弹出选择对话框进行选择
		Set<String> clients = REDIS_CLIENT.keySet();
		Optional<String> result = AlertUtil.choice("选择客户端", "请选择连接客户端类型：", clients);
		if(result.isPresent()) {
			appLogger.info("用户选择客户端："+result.get());
			return REDIS_CLIENT.get(result.get());
		}

		return null;
	}

	/**
	 * 新建连接按钮监听
	 * @param e	事件对象
	 * @throws IOException　redis连接操作可能发生异常
	 */
	public void createConnection(ActionEvent e) throws IOException{
		appLogger.info("检查是否存在已连接的客户端。");
		//如果redis客户端实例不为空则先关闭之前的连接
		if(redisClient != null) {
			appLogger.info("关闭已连接的客户端。");
			redisClient.close();
		}

		//选择redis客户端
		this.redisClient = this.choiceClient();
		if(redisClient != null) {
			//初始化redis连接和组件
			if(redisClient.init()){
				cmdTextArea.setText("已连接！\n");
				initTreeView();
			}
		}
	}

	/**
	 * 初始化TreeView树
	 */
	protected void initTreeView() {
		appLogger.info("初始化Redis树结构。");
		TreeItem<RedisTreeNode> root = this.getRootTreeItem();
		redisTreeView.setRoot(root);

		appLogger.info("添加节点选择监听事件！");
		//节点选择事件处理
		redisTreeView.getSelectionModel().selectedItemProperty().addListener((observable,oldValue,newValue)->{
			exeService.submit(()->{
				appLogger.info("Redis树节点被点击 -> oldValue:"+oldValue+",newValue:"+newValue);
				TreeItem<RedisTreeNode> currentSelectedItem = (TreeItem<RedisTreeNode>) newValue;
				if(currentSelectedItem==null) return;

				//设置节点信息
				RedisTreeNode selectedNode = currentSelectedItem.getValue();
				String itemInfo = this.getItemInfo(selectedNode);
				infoTextArea.setText(itemInfo);

				if(!selectedNode.isLeaf() && !selectedNode.isLoaded()) {
					//加载子节点
					currentSelectedItem.getChildren().addAll(this.getChildren(selectedNode));
					currentSelectedItem.setExpanded(true);
				}

				//标记节点已加载过
				selectedNode.setLoaded(true);
			});
		});

		initRedisTreeContextMenu();

		appLogger.info("Redis树结构初始化完成！");
	}

	protected void initRedisTreeContextMenu() {
		appLogger.info("设置树节点右键菜单！");

		//设置TreeView树右键菜单
		refreshItem.setOnAction((event)->{
			exeService.submit(()->{
				appLogger.info("执行[刷新]动作！");
				TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
				RedisTreeNode redisTreeNode = selectedItem.getValue();
				if(redisTreeNode instanceof RedisKeyTreeNode) {
					infoTextArea.setText(getItemInfo(redisTreeNode));
				}else {
					selectedItem.getChildren().clear();
					selectedItem.getChildren().addAll(this.getChildren(redisTreeNode));
					selectedItem.setExpanded(true);
				}
			});
		});

		disconnectItem.setOnAction((event)->{
			appLogger.info("执行[断开连接]动作！");
			try {
				redisClient.close();
				redisTreeView.setRoot(null);
				redisTreeView.setContextMenu(null);
				infoTextArea.clear();
				cmdTextArea.setText("连接已断开！\n");
			} catch (IOException e) {
				AlertUtil.exception(e);
			}

		});

		clearDBItem.setOnAction((event)->{
			appLogger.info("执行[清理DB]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisDBTreeNode) {
				int db = ((RedisDBTreeNode) redisTreeNode).getDb();
				redisClient.flushDB(db);
				selectedItem.getChildren().clear();
			}
		});

		createKeyItem.setOnAction((event)->{
			appLogger.info("执行[创建键]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisDBTreeNode) {
				int db = ((RedisDBTreeNode) redisTreeNode).getDb();
				try {
					ComboBox<String> typeComboBox = new ComboBox<String>(FXCollections.observableArrayList(
							KEY_TYPE_STRING,KEY_TYPE_SET,KEY_TYPE_ZSET,KEY_TYPE_LIST,KEY_TYPE_HASH));
					typeComboBox.getSelectionModel().select(KEY_TYPE_STRING);
					TextField nameTextField = new TextField();
					TextField expireTextField = new TextField();
					TextArea valueTextArea = new TextArea();
					FormDialog createKeyDialog = new FormDialog("创建键",
							new FormDialog.Item("类型：", typeComboBox),
							new FormDialog.Item("键名：", nameTextField),
							new FormDialog.Item("过期时间(秒)：", expireTextField),
							new FormDialog.Item("值(非字符串结构用json表示)：", valueTextArea));
					Optional<Boolean> result = createKeyDialog.showAndWait();
					if(result.isPresent()) {
						String type = typeComboBox.getValue();
						String name = nameTextField.getText();
						String value = valueTextArea.getText();
						String expire = expireTextField.getText();
						if(KEY_TYPE_STRING.equals(type)) {
							redisClient.set(db,name, value);
						}else if(KEY_TYPE_SET.equals(type)) {
							try {
								if(JSONValidator.from(value).validate()){
									JSONArray data = JSON.parseArray(value);
									redisClient.sadd(db,name, data.toArray(new String[data.size()]));
								}else {
									redisClient.sadd(db,name, value);
								}
							} catch (Exception e) {
								//非json字符串
								redisClient.sadd(db,name, value);
							}
						}else if(KEY_TYPE_ZSET.equals(type)) {
							redisClient.zadd(db,name, JSON.parseObject(value,new TypeReference<Map<String,Double>>(){}));
						}else if(KEY_TYPE_LIST.equals(type)) {
							JSONArray data = JSON.parseArray(value);
							redisClient.lpush(db,name, data.toArray(new String[data.size()]));
						}else if(KEY_TYPE_HASH.equals(type)) {
							redisClient.hset(db,name, JSON.parseObject(value,new TypeReference<Map<String,String>>(){}));
						}
						if(expire!=null&&!"".equals(expire.trim())) {
							redisClient.expire(db,name, Integer.valueOf(expire));
						}
						selectedItem.getChildren().add(new TreeItem<RedisTreeNode>(new RedisKeyTreeNode(db, type, name)));
					}
				} catch (Exception e) {
					AlertUtil.exception(e);
				}
			}
		});

		delKeyItem.setOnAction((event)->{
			appLogger.info("执行[删除键]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				redisClient.del(db,key);

				selectedItem.getParent().getChildren().remove(selectedItem);
			}
		});

		expireItem.setOnAction((event)->{
			appLogger.info("执行[设置有效期]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				Optional<String> result = AlertUtil.input("设置有效期","请在下方输入框内输入新的有效期(单位：秒)：");
				if(result.isPresent()) {
					int expire = Integer.parseInt(result.get());
					redisClient.expire(db,key,expire);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		persistItem.setOnAction((event)->{
			appLogger.info("执行[清除有效期]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				redisClient.persist(db,key);
				//更新节点信息
				infoTextArea.setText(this.getItemInfo(redisTreeNode));
			}
		});

		renameItem.setOnAction((event)->{
			appLogger.info("执行[重命名]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				Optional<String> result = AlertUtil.input("重命名", "请输入键的新名称！");
				if(result.isPresent()) {
					String newName = result.get();
					redisClient.rename(db,key,newName);
					TreeItem<RedisTreeNode> parentItem = selectedItem.getParent();
					parentItem.getChildren().remove(selectedItem);
					parentItem.getChildren().add(selectedItem);
					((RedisKeyTreeNode) redisTreeNode).setKey(newName);
				}
			}
		});

		setItem.setOnAction((event)->{
			appLogger.info("执行[设置值]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextArea valueTextArea = new TextArea(redisClient.get(db,key));
				FormDialog formDialog = new FormDialog("设置值",new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					String newValue = valueTextArea.getText();
					redisClient.set(db, key, newValue);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		saddItem.setOnAction((event)->{
			appLogger.info("执行[增加项]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextArea valueTextArea = new TextArea();
				FormDialog formDialog = new FormDialog("增加项",new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					String value = valueTextArea.getText();
					redisClient.sadd(db, key, value);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		sremItem.setOnAction((event)->{
			appLogger.info("执行[删除项]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				Optional<String> result = AlertUtil.input("删除项", "请输入删除项名称！");
				if(result.isPresent()) {
					redisClient.srem(db,key,result.get());
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		zaddItem.setOnAction((event)->{
			appLogger.info("执行[增加项]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextField scoreTextField = new TextField();
				TextArea valueTextArea = new TextArea();
				FormDialog formDialog = new FormDialog("增加项",
						new FormDialog.Item("分数:", scoreTextField),
						new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					Double score = Double.parseDouble(scoreTextField.getText());
					String value = valueTextArea.getText();
					Map<String,Double> data = new HashMap<>(1);
					data.put(value, score);
					redisClient.zadd(db, key, data);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		zremItem.setOnAction((event)->{
			appLogger.info("执行[删除项]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				Optional<String> result = AlertUtil.input("删除项", "请输入删除项名称！");
				if(result.isPresent()) {
					redisClient.zrem(db,key,result.get());
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		lsetItem.setOnAction((event)->{
			appLogger.info("执行[设置元素]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextField indexTextField = new TextField();
				TextArea valueTextArea = new TextArea();
				FormDialog formDialog = new FormDialog("设置元素",
						new FormDialog.Item("位置:", indexTextField),
						new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					long index = Long.parseLong(indexTextField.getText());
					String value = valueTextArea.getText();
					redisClient.lset(db,key,index,value);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		linsertItem.setOnAction((event)->{
			appLogger.info("执行[元素左/右插入]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				String before = "左侧";
				String after = "右侧";
				TextField refItemTextField = new TextField();
				ComboBox<String> posComboBox = new ComboBox<>(FXCollections.observableArrayList(Arrays.asList(before,after)));
				TextArea valueTextArea = new TextArea();
				FormDialog formDialog = new FormDialog("元素左/右插入",
						new FormDialog.Item("参考元素:", refItemTextField),
						new FormDialog.Item("相对位置:", posComboBox),
						new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					ListPosition where = null;
					if(before.equals(posComboBox.getValue())){
						where = ListPosition.BEFORE;
					}else if(after.equals(posComboBox.getValue())) {
						where = ListPosition.AFTER;
					}
					String pivot = refItemTextField.getText();
					String value = valueTextArea.getText();
					redisClient.linsert(db, key, where, pivot, value);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		lpushItem.setOnAction((event)->{
			appLogger.info("执行[左侧插入]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextArea valueTextArea = new TextArea(redisClient.get(db,key));
				FormDialog formDialog = new FormDialog("左侧插入",new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					String value = valueTextArea.getText();
					redisClient.lpush(db,key,value);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		lpopItem.setOnAction((event)->{
			appLogger.info("执行[左侧弹出]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				redisClient.lpop(db,key);
				//更新节点信息
				infoTextArea.setText(this.getItemInfo(redisTreeNode));
			}
		});

		rpushItem.setOnAction((event)->{
			appLogger.info("执行[右侧插入]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextArea valueTextArea = new TextArea(redisClient.get(db,key));
				FormDialog formDialog = new FormDialog("右侧插入",new FormDialog.Item("值:", valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					String value = valueTextArea.getText();
					redisClient.rpush(db,key,value);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		rpopItem.setOnAction((event)->{
			appLogger.info("执行[右侧弹出]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				redisClient.rpop(db,key);
				//更新节点信息
				infoTextArea.setText(this.getItemInfo(redisTreeNode));
			}
		});

		lremItem.setOnAction((event)->{
			appLogger.info("执行[移除元素]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				Optional<String> result = AlertUtil.input("删除项", "请输入移除元素名称！");
				if(result.isPresent()) {
					redisClient.lrem(db,key,result.get());
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		hsetItem.setOnAction((event)->{
			appLogger.info("执行[设置项]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				TextField itemTextField = new TextField();
				TextArea valueTextArea = new TextArea();
				FormDialog formDialog = new FormDialog("设置项",
						new FormDialog.Item("项:", itemTextField),
						new FormDialog.Item("值:",valueTextArea));
				Optional<Boolean> result = formDialog.showAndWait();
				if(result.isPresent()) {
					String item = itemTextField.getText();
					String value = itemTextField.getText();
					Map<String,String> data = new HashMap<>();
					data.put(item, value);
					redisClient.hset(db, key, data);
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		hdelItem.setOnAction((event)->{
			appLogger.info("执行[删除项]动作！");
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisKeyTreeNode) {
				int db = ((RedisKeyTreeNode) redisTreeNode).getDb();
				String key = ((RedisKeyTreeNode) redisTreeNode).getKey();
				Optional<String> result = AlertUtil.input("删除项", "请输入移除元素名称！");
				if(result.isPresent()) {
					redisClient.hdel(db,key,result.get());
					//更新节点信息
					infoTextArea.setText(this.getItemInfo(redisTreeNode));
				}
			}
		});

		redisTreeView.setContextMenu(new ContextMenu(disconnectItem));
		redisTreeView.setOnContextMenuRequested((event)->{
			redisTreeView.getContextMenu().getItems().clear();
			redisTreeView.getContextMenu().getItems().add(refreshItem);
			TreeItem<RedisTreeNode> selectedItem = redisTreeView.getSelectionModel().getSelectedItem();
			RedisTreeNode redisTreeNode = selectedItem.getValue();
			if(redisTreeNode instanceof RedisRootTreeNode) {
				redisTreeView.getContextMenu().getItems().add(disconnectItem);
			}else if(redisTreeNode instanceof RedisDBTreeNode) {
				redisTreeView.getContextMenu().getItems().addAll(createKeyItem,clearDBItem);
			}else if(redisTreeNode instanceof RedisKeyTreeNode) {
				String type = ((RedisKeyTreeNode) redisTreeNode).getType();
				if(KEY_TYPE_STRING.equals(type)) {
					redisTreeView.getContextMenu().getItems().addAll(setItem);
				}else if(KEY_TYPE_SET.equals(type)){
					redisTreeView.getContextMenu().getItems().addAll(saddItem,sremItem);
				}else if(KEY_TYPE_ZSET.equals(type)) {
					redisTreeView.getContextMenu().getItems().addAll(zaddItem,zremItem);
				}else if(KEY_TYPE_LIST.equals(type)) {
					redisTreeView.getContextMenu().getItems().addAll(lsetItem,linsertItem,lpushItem,lpopItem,rpushItem,rpopItem,lremItem);
				}else if(KEY_TYPE_HASH.equals(type)) {
					redisTreeView.getContextMenu().getItems().addAll(hsetItem,hdelItem);
				}
				redisTreeView.getContextMenu().getItems().addAll(renameItem,expireItem,persistItem,delKeyItem);
			}
		});
	};

	/**
	 * 获取根节点
	 * @return　根节点对象
	 */
	protected TreeItem<RedisTreeNode> getRootTreeItem() {
		appLogger.info("获取Redis树根节点！");
		RedisRootTreeNode rootNode = new RedisRootTreeNode(redisClient.getConnStr());
		TreeItem<RedisTreeNode> rootItem = new TreeItem<RedisTreeNode>(rootNode);
		rootItem.setExpanded(true);
		rootItem.getChildren().addAll(this.getChildren(rootNode));
		return rootItem;
	}

	/**
	 * 获取子节点集合
	 * @param selectedNode　当前选中节点
	 * @return　子节点集合
	 */
	protected List<TreeItem<RedisTreeNode>> getChildren(RedisTreeNode selectedNode) {
		appLogger.info("获取Redis树子节点！当前节点："+selectedNode);
		if(selectedNode instanceof RedisRootTreeNode) {
			List<TreeItem<RedisTreeNode>> dbNodeList = new ArrayList<>();
			for(int i=0;i<Integer.MAX_VALUE;i++) {
				try {
					redisClient.select(i);
				} catch (Exception e) {
					break;
				}
				RedisDBTreeNode dbNode = new RedisDBTreeNode(i);
				TreeItem<RedisTreeNode> dbItem = new TreeItem<RedisTreeNode>(dbNode);
				dbNodeList.add(dbItem);
			}
			return dbNodeList;
		}else if(selectedNode instanceof RedisDBTreeNode) {
			RedisDBTreeNode dbRedisTreeNode = (RedisDBTreeNode)selectedNode;
			int db = dbRedisTreeNode.getDb();
			Set<String> keys=redisClient.keys(db,"*");
			List<TreeItem<RedisTreeNode>> keyNodeList = new ArrayList<>();
			for(String key:keys) {
				String type = redisClient.type(db,key);
				RedisKeyTreeNode keyNode = new RedisKeyTreeNode(db, type, key);
				TreeItem<RedisTreeNode> keyItem = new TreeItem<RedisTreeNode>(keyNode);
				keyNodeList.add(keyItem);
			}
			return keyNodeList;
		}
		return null;
	}

	private static final String convertByte2Str(byte[] byteStr){
		return new String(byteStr,StandardCharsets.UTF_8);
	}

	/**
	 * 获取节点信息
	 * @param selectedNode　当前选中节点
	 * @return　节点信息
	 */
	protected String getItemInfo(RedisTreeNode selectedNode) {
		appLogger.info("获取Redis树节点详情信息。当前节点："+selectedNode);
		if(selectedNode instanceof RedisRootTreeNode) {
			return redisClient.getRedisInfo();
		}else if(selectedNode instanceof RedisDBTreeNode) {
			RedisDBTreeNode dbNode = (RedisDBTreeNode)selectedNode;
			int db = dbNode.getDb();
			return redisClient.getDBInfo(db);
		}else if(selectedNode instanceof RedisKeyTreeNode) {
			RedisKeyTreeNode keyNode = (RedisKeyTreeNode)selectedNode;
			int db = keyNode.getDb();
			String type = keyNode.getType();
			String key = keyNode.getKey();
			byte[] byteKey = key.getBytes(StandardCharsets.UTF_8);
			Long ttl = redisClient.ttl(db,key);
			StringBuilder keyInfo = new StringBuilder();
			keyInfo.append("TYPE: "+type+"\tTTL: "+ttl+"\n");
			keyInfo.append("------------------------------------------------------------\n");
			if(KEY_TYPE_STRING.equalsIgnoreCase(type)) {
				byte[] resultStr=redisClient.get(db, byteKey);
				keyInfo.append(convertByte2Str(resultStr));
			}else if(KEY_TYPE_SET.equalsIgnoreCase(type)) {
				Set<byte[]> valueSet=redisClient.smembers(db,byteKey);
				Set<String> newValueSet=new HashSet<>();
				for(byte[] value:valueSet){
					newValueSet.add(convertByte2Str(value));
				}
				keyInfo.append(JSON.toJSONString(newValueSet, true));
			}else if(KEY_TYPE_ZSET.equalsIgnoreCase(type)){
				Map<String,Double> zsetReuslt = new HashMap<String, Double>();
				for(Tuple item:redisClient.zrangeWithScores(db,key, 0, -1)) {
					zsetReuslt.put(item.getElement(), item.getScore());
				}
				keyInfo.append(JSON.toJSONString(zsetReuslt, true));
			}else if(KEY_TYPE_LIST.equalsIgnoreCase(type)) {
				List<byte[]> valueList=redisClient.lrange(db,byteKey, 0, -1);
				List<String> newValueList=new ArrayList<>();
				for(byte[] valueStr:valueList){
					newValueList.add(convertByte2Str(valueStr));
				}
				keyInfo.append(JSON.toJSONString(newValueList, true));
			}else if(KEY_TYPE_HASH.equalsIgnoreCase(type)) {
				Map<byte[],byte[]> valueMap=redisClient.hgetAll(db,byteKey);
				Map<String,String> newValueMap=new HashMap<>();
				for(byte[] keyStr:valueMap.keySet()){
					byte[] valueStr=valueMap.get(keyStr);
					newValueMap.put(convertByte2Str(keyStr), convertByte2Str(valueStr));
				}
				keyInfo.append(JSON.toJSONString(newValueMap, true));
			}
			return keyInfo.toString();
		}
		return null;
	}

	/**
	 * 执行命令动作监听
	 * @param e　事件对象
	 */
	public void exeCmd(ActionEvent e) {
		if(redisClient == null) {
			appLogger.error("无法执行用户输入命令！【未建立连接】");
			return;
		}

		String cmdStr = cmdTextField.getText();
		if(cmdStr == null || "".equals(cmdStr.trim())) {
			appLogger.error("无法执行用户输入命令！【命令为空】");
			return;
		}

		appLogger.info("执行用户输入命令："+cmdStr);
		//执行redis命令
		String result = redisClient.exeCmd(cmdStr);
		//清空命令输入框
		cmdTextField.clear();
		//将命令和响应追加到命令执行历史内
		cmdTextArea.appendText("> "+cmdStr+"\n");
		cmdTextArea.appendText(result+"\n");
	}
}
